//
//  Popover.swift
//  Popover
//
//  Created by corin8823 on 8/16/15.
//  Copyright (c) 2015 corin8823. All rights reserved.
//

import Foundation
import UIKit

public enum PopoverOption {
    case arrowSize(CGSize)
    case animationIn(TimeInterval)
    case animationOut(TimeInterval)
    case cornerRadius(CGFloat)
    case sideEdge(CGFloat)
    case blackOverlayColor(UIColor)
    case overlayBlur(UIBlurEffect.Style)
    case type(PopoverType)
    case subType(PopoverSubType)
    case color(UIColor)
    case dismissOnBlackOverlayTap(Bool)
    case showBlackOverlay(Bool)
    case springDamping(CGFloat)
    case initialSpringVelocity(CGFloat)
    case sideOffset(CGFloat)
    case borderColor(UIColor)
    case bgColor(UIColor)
    case shadowColor(UIColor)
}

@objc public enum PopoverType: Int {
    case up
    case down
    case left
    case right
    case auto
}

@objc public enum PopoverSubType: Int {
    case none
    case up
    case down
    case left
    case right
}

@objcMembers
open class Popover: UIView {
    
    // custom property
    open var arrowSize: CGSize = CGSize(width: 16.0, height: 10.0)
    open var animationIn: TimeInterval = 0.6
    open var animationOut: TimeInterval = 0.3
    open var cornerRadius: CGFloat = 6.0
    open var sideEdge: CGFloat = 20.0
    open var popoverType: PopoverType = .down
    open var popoverSubType: PopoverSubType = .none
    open var blackOverlayColor: UIColor = UIColor(white: 0.0, alpha: 0.2)
    open var overlayBlur: UIBlurEffect?
    open var popoverColor: UIColor = UIColor.white
    open var dismissOnBlackOverlayTap: Bool = true
    open var showBlackOverlay: Bool = true
    open var highlightFromView: Bool = false
    open var highlightCornerRadius: CGFloat = 0
    open var springDamping: CGFloat = 0.7
    open var initialSpringVelocity: CGFloat = 3
    open var sideOffset: CGFloat = 6.0
    open var borderColor: UIColor?
    open var bgColor: UIColor = UIColor.clear
    open var shadowColor: UIColor?
    
    // custom closure
    open var willShowHandler: (() -> ())?
    open var willDismissHandler: (() -> ())?
    open var didShowHandler: (() -> ())?
    open var didDismissHandler: (() -> ())?
    
    public fileprivate(set) var blackOverlay: UIControl = UIControl()
    
    fileprivate var containerView: UIView!
    fileprivate var contentView: UIView!
    fileprivate var contentViewFrame: CGRect!
    fileprivate var arrowShowPoint: CGPoint!
    
    public init() {
        super.init(frame: .zero)
        self.backgroundColor = .clear
        self.accessibilityViewIsModal = true
    }
    
    public init(showHandler: (() -> ())?, dismissHandler: (() -> ())?) {
        super.init(frame: .zero)
        self.backgroundColor = .clear
        self.didShowHandler = showHandler
        self.didDismissHandler = dismissHandler
        self.accessibilityViewIsModal = true
    }
    
    public init(options: [PopoverOption]?, showHandler: (() -> ())? = nil, dismissHandler: (() -> ())? = nil) {
        super.init(frame: .zero)
        self.backgroundColor = .clear
        self.setOptions(options)
        self.didShowHandler = showHandler
        self.didDismissHandler = dismissHandler
        self.accessibilityViewIsModal = true
    }
    
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override open func layoutSubviews() {
        super.layoutSubviews()
        self.contentView.frame = self.bounds
    }
    
    open func showAsDialog(_ contentView: UIView) {
        guard let rootView = UIApplication.appKeyWindow else {
            return
        }
        self.showAsDialog(contentView, inView: rootView)
    }
    
    open func showAsDialog(_ contentView: UIView, inView: UIView) {
        showAsDialog(contentView, inView: inView, yOffset: 0)
    }
    
    open func showAsDialog(_ contentView: UIView, inView: UIView, yOffset: CGFloat) {
        self.arrowSize = .zero
        
        var yCenter: CGFloat = (inView.center.y - contentView.frame.height / 2) + yOffset
        yCenter = max(0, yCenter)
        let point = CGPoint(x: inView.center.x, y: yCenter)
        self.show(contentView, point: point, inView: inView)
    }
    
    open func show(_ contentView: UIView, fromView: UIView) {
        guard let rootView = UIApplication.appKeyWindow else {
            return
        }
        self.show(contentView, fromView: fromView, inView: rootView)
    }
    
    open func show(_ contentView: UIView, fromView: UIView, inView: UIView) {
        var point: CGPoint
        
        //TODO: add left/right auto
        if self.popoverType == .auto {
            if let point = fromView.superview?.convert(fromView.frame.origin, to: nil),
               point.y + fromView.frame.height + self.arrowSize.height + contentView.frame.height > inView.frame.height {
                self.popoverType = .up
            } else {
                self.popoverType = .down
            }
        }
        
        let fromViewWidth: CGFloat = (fromView.frame.size.width / 2)
        let contentViewWidth: CGFloat = contentView.frame.width / 2
        switch self.popoverType {
        case .up:
            point = inView.convert(
                CGPoint(
                    x: fromView.frame.origin.x + fromViewWidth,
                    y: fromView.frame.origin.y
                ), from: fromView.superview)
            
            if arrowSize != .zero {
                break
            }
            switch popoverSubType {
            case .none:
                break
            case .up:
                break
            case .down:
                break
            case .left:
                point.x += contentViewWidth
                
            case .right:
                point.x -= contentViewWidth
            }
            
        case .down, .auto:
            point = inView.convert(
                CGPoint(
                    x: fromView.frame.origin.x + fromViewWidth,
                    y: fromView.frame.origin.y + fromView.frame.size.height
                ), from: fromView.superview)
            
            if arrowSize != .zero {
                break
            }
            switch popoverSubType {
            case .none:
                break
            case .up:
                break
            case .down:
                break
            case .left:
                point.x += contentViewWidth
                
            case .right:
                point.x -= contentViewWidth
            }
            
        case .left:
            point = inView.convert(
                CGPoint(x: fromView.frame.origin.x - sideOffset,
                        y: fromView.frame.origin.y + 0.5 * fromView.frame.height
                       ), from: fromView.superview)
        case .right:
            point = inView.convert(
                CGPoint(x: fromView.frame.origin.x + fromView.frame.size.width + sideOffset,
                        y: fromView.frame.origin.y + 0.5 * fromView.frame.height
                       ), from: fromView.superview)
        }
        
        if self.highlightFromView {
            self.createHighlightLayer(fromView: fromView, inView: inView)
        }
        
        self.show(contentView, point: point, inView: inView)
    }
    
    open func show(_ contentView: UIView, point: CGPoint) {
        guard let rootView = UIApplication.appKeyWindow else {
            return
        }
        self.show(contentView, point: point, inView: rootView)
    }
    
    open func show(_ contentView: UIView, point: CGPoint, inView: UIView) {
        if self.dismissOnBlackOverlayTap || self.showBlackOverlay {
            self.blackOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            self.blackOverlay.frame = inView.bounds
            inView.addSubview(self.blackOverlay)
            
            if showBlackOverlay {
                if let overlayBlur = self.overlayBlur {
                    let effectView = UIVisualEffectView(effect: overlayBlur)
                    effectView.frame = self.blackOverlay.bounds
                    effectView.isUserInteractionEnabled = false
                    self.blackOverlay.addSubview(effectView)
                } else {
                    if !self.highlightFromView {
                        self.blackOverlay.backgroundColor = self.blackOverlayColor
                    }
                    self.blackOverlay.alpha = 0
                }
            }
            
            if self.dismissOnBlackOverlayTap {
                self.blackOverlay.addTarget(self, action: #selector(Popover.dismiss), for: .touchUpInside)
            }
        }
        
        self.containerView = inView
        self.contentView = contentView
        self.contentView.backgroundColor = bgColor
        self.contentView.layer.cornerRadius = self.cornerRadius
        if let tShadowColor = shadowColor {
            self.contentView.layer.masksToBounds = false
            self.contentView.layer.shadowColor = tShadowColor.cgColor
            self.contentView.layer.shadowOffset = CGSize(width: -1, height: 1)
            self.contentView.layer.shadowOpacity = 1
        } else {
            self.contentView.layer.masksToBounds = true
        }
        self.arrowShowPoint = point
        self.show()
    }
    
    open override func accessibilityPerformEscape() -> Bool {
        self.dismiss()
        return true
    }
    
    @objc open func dismiss() {
        if self.superview != nil {
            self.willDismissHandler?()
            UIView.animate(withDuration: self.animationOut, delay: 0,
                           options: UIView.AnimationOptions(),
                           animations: {
                self.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001)
                self.blackOverlay.alpha = 0
            }){ _ in
                self.contentView.removeFromSuperview()
                self.blackOverlay.removeFromSuperview()
                self.removeFromSuperview()
                self.transform = CGAffineTransform.identity
                self.didDismissHandler?()
            }
        }
    }
    
    override open func draw(_ rect: CGRect) {
        super.draw(rect)
        let arrow = UIBezierPath()
        let color = self.popoverColor
        let arrowPoint = self.containerView.convert(self.arrowShowPoint, to: self)
        switch self.popoverType {
        case .up:
            arrow.move(to: CGPoint(x: arrowPoint.x, y: self.bounds.height))
            arrow.addLine(
                to: CGPoint(
                    x: arrowPoint.x - self.arrowSize.width * 0.5,
                    y: self.isCornerLeftArrow ? self.arrowSize.height : self.bounds.height - self.arrowSize.height
                )
            )
            
            arrow.addLine(to: CGPoint(x: self.cornerRadius, y: self.bounds.height - self.arrowSize.height))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.cornerRadius,
                    y: self.bounds.height - self.arrowSize.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(90),
                endAngle: self.radians(180),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: 0, y: self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.cornerRadius,
                    y: self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(180),
                endAngle: self.radians(270),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: 0))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width - self.cornerRadius,
                    y: self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(270),
                endAngle: self.radians(0),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - self.arrowSize.height - self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width - self.cornerRadius,
                    y: self.bounds.height - self.arrowSize.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(0),
                endAngle: self.radians(90),
                clockwise: true)
            
            arrow.addLine(
                to: CGPoint(
                    x: arrowPoint.x + self.arrowSize.width * 0.5,
                    y: self.isCornerRightArrow ? self.arrowSize.height : self.bounds.height - self.arrowSize.height
                )
            )
            
        case .down, .auto:
            arrow.move(to: CGPoint(x: arrowPoint.x, y: 0))
            
            if self.isCloseToCornerRightArrow && !self.isCornerRightArrow {
                if !isBehindCornerRightArrow {
                    arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height))
                    arrow.addArc(
                        withCenter: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height + self.cornerRadius),
                        radius: self.cornerRadius,
                        startAngle: self.radians(270.0),
                        endAngle: self.radians(0),
                        clockwise: true)
                } else {
                    arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.arrowSize.height + self.cornerRadius))
                    arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.arrowSize.height))
                }
            } else {
                arrow.addLine(
                    to: CGPoint(
                        x: self.isBehindCornerLeftArrow ? self.frame.minX - self.arrowSize.width * 0.5 : arrowPoint.x + self.arrowSize.width * 0.5,
                        y: self.isCornerRightArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height
                    )
                )
                arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height))
                arrow.addArc(
                    withCenter: CGPoint(
                        x: self.bounds.width - self.cornerRadius,
                        y: self.arrowSize.height + self.cornerRadius
                    ),
                    radius: self.cornerRadius,
                    startAngle: self.radians(270.0),
                    endAngle: self.radians(0),
                    clockwise: true)
            }
            
            arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width - self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(0),
                endAngle: self.radians(90),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: 0, y: self.bounds.height))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(90),
                endAngle: self.radians(180),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: 0, y: self.arrowSize.height + self.cornerRadius))
            
            if !isBehindCornerLeftArrow {
                arrow.addArc(
                    withCenter: CGPoint(
                        x: self.cornerRadius,
                        y: self.arrowSize.height + self.cornerRadius
                    ),
                    radius: self.cornerRadius,
                    startAngle: self.radians(180),
                    endAngle: self.radians(270),
                    clockwise: true)
            }
            
            if isBehindCornerRightArrow {
                arrow.addLine(to: CGPoint(
                    x: self.bounds.width - self.arrowSize.width * 0.5,
                    y: self.isCornerLeftArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height))
            } else if isCloseToCornerLeftArrow && !isCornerLeftArrow {
                () // skipping this line in that case
            } else {
                arrow.addLine(to: CGPoint(x: arrowPoint.x - self.arrowSize.width * 0.5,
                                          y: self.isCornerLeftArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height))
            }
            
        case .left:
            arrow.move(to: CGPoint(x: self.bounds.width, y: self.bounds.height * 0.5))
            arrow.addLine(
                to: CGPoint(
                    x: self.bounds.width - self.arrowSize.height,
                    y: self.bounds.height * 0.5 + self.arrowSize.width * 0.5
                ))
            
            arrow.addLine(to: CGPoint(x:self.bounds.width - self.arrowSize.height, y: self.bounds.height - self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width - self.arrowSize.height - self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(0.0),
                endAngle: self.radians(90),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: self.cornerRadius, y: self.bounds.height))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(90),
                endAngle: self.radians(180),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: 0, y: self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.cornerRadius,
                    y: self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(180),
                endAngle: self.radians(270),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width - self.arrowSize.height - self.cornerRadius, y: 0))
            arrow.addArc(
                withCenter: CGPoint(x: self.bounds.width - self.arrowSize.height - self.cornerRadius,
                                    y: self.cornerRadius
                                   ),
                radius: self.cornerRadius,
                startAngle: self.radians(270),
                endAngle: self.radians(0),
                clockwise: true)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width - self.arrowSize.height,
                                      y: self.bounds.height * 0.5 - self.arrowSize.width * 0.5
                                     ))
        case .right:
            arrow.move(to: CGPoint(x: arrowPoint.x, y: self.bounds.height * 0.5))
            arrow.addLine(
                to: CGPoint(
                    x: arrowPoint.x + self.arrowSize.height,
                    y: self.bounds.height * 0.5 + 0.5 * self.arrowSize.width
                ))
            
            arrow.addLine(
                to: CGPoint(
                    x: arrowPoint.x + self.arrowSize.height,
                    y: self.bounds.height - self.cornerRadius
                ))
            arrow.addArc(
                withCenter: CGPoint(
                    x: arrowPoint.x + self.arrowSize.height + self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(180.0),
                endAngle: self.radians(90),
                clockwise: false)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width + arrowPoint.x - self.cornerRadius, y: self.bounds.height))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width + arrowPoint.x - self.cornerRadius,
                    y: self.bounds.height - self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(90),
                endAngle: self.radians(0),
                clockwise: false)
            
            arrow.addLine(to: CGPoint(x: self.bounds.width + arrowPoint.x, y: self.cornerRadius))
            arrow.addArc(
                withCenter: CGPoint(
                    x: self.bounds.width + arrowPoint.x - self.cornerRadius,
                    y: self.cornerRadius
                ),
                radius: self.cornerRadius,
                startAngle: self.radians(0),
                endAngle: self.radians(-90),
                clockwise: false)
            
            arrow.addLine(to: CGPoint(x: arrowPoint.x + self.arrowSize.height - self.cornerRadius, y: 0))
            arrow.addArc(
                withCenter: CGPoint(x: arrowPoint.x + self.arrowSize.height + self.cornerRadius,
                                    y: self.cornerRadius
                                   ),
                radius: self.cornerRadius,
                startAngle: self.radians(-90),
                endAngle: self.radians(-180),
                clockwise: false)
            
            arrow.addLine(to: CGPoint(x: arrowPoint.x + self.arrowSize.height,
                                      y:  self.bounds.height * 0.5 - self.arrowSize.width * 0.5))
        }
        
        color.setFill()
        arrow.fill()
        if let borderColor = borderColor {
            borderColor.setStroke()
            arrow.stroke()
        }
    }
}

private extension Popover {
    
    func setOptions(_ options: [PopoverOption]?){
        if let options = options {
            for option in options {
                switch option {
                case let .arrowSize(value):
                    self.arrowSize = value
                case let .animationIn(value):
                    self.animationIn = value
                case let .animationOut(value):
                    self.animationOut = value
                case let .cornerRadius(value):
                    self.cornerRadius = value
                case let .sideEdge(value):
                    self.sideEdge = value
                case let .blackOverlayColor(value):
                    self.blackOverlayColor = value
                case let .overlayBlur(style):
                    self.overlayBlur = UIBlurEffect(style: style)
                case let .type(value):
                    self.popoverType = value
                case let .subType(value):
                    self.popoverSubType = value
                case let .color(value):
                    self.popoverColor = value
                case let .dismissOnBlackOverlayTap(value):
                    self.dismissOnBlackOverlayTap = value
                case let .showBlackOverlay(value):
                    self.showBlackOverlay = value
                case let .springDamping(value):
                    self.springDamping = value
                case let .initialSpringVelocity(value):
                    self.initialSpringVelocity = value
                case let .sideOffset(value):
                    self.sideOffset = value
                case let .borderColor(value):
                    self.borderColor = value
                    
                case .bgColor(let value):
                    self.bgColor = value
                case let .shadowColor(value):
                    self.shadowColor = value
                }
            }
        }
    }
    
    func create() {
        var frame = self.contentView.frame
        
        switch self.popoverType {
        case .up, .down, .auto:
            frame.origin.x = self.arrowShowPoint.x - frame.size.width * 0.5
        case .left, .right:
            frame.origin.y = self.arrowShowPoint.y - frame.size.height * 0.5
        }
        
        var sideEdge: CGFloat = 0.0
        if frame.size.width < self.containerView.frame.size.width {
            sideEdge = self.sideEdge
        }
        
        let outerSideEdge = frame.maxX - self.containerView.bounds.size.width
        if outerSideEdge > 0 {
            frame.origin.x -= (outerSideEdge + sideEdge)
        } else {
            if frame.minX < 0 {
                frame.origin.x += abs(frame.minX) + sideEdge
            }
        }
        self.frame = frame
        
        let arrowPoint = self.containerView.convert(self.arrowShowPoint, to: self)
        var anchorPoint: CGPoint
        switch self.popoverType {
        case .up:
            frame.origin.y = self.arrowShowPoint.y - frame.height - self.arrowSize.height
            anchorPoint = CGPoint(x: arrowPoint.x / frame.size.width, y: 1)
        case .down, .auto:
            frame.origin.y = self.arrowShowPoint.y
            anchorPoint = CGPoint(x: arrowPoint.x / frame.size.width, y: 0)
        case .left:
            frame.origin.x = self.arrowShowPoint.x - frame.size.width - self.arrowSize.height
            anchorPoint = CGPoint(x: 1, y: 0.5)
        case .right:
            frame.origin.x = self.arrowShowPoint.x
            anchorPoint = CGPoint(x: 0, y: 0.5)
        }
        
        if self.arrowSize == .zero {
            anchorPoint = CGPoint(x: 0.5, y: 0.5)
        }
        
        let lastAnchor = self.layer.anchorPoint
        self.layer.anchorPoint = anchorPoint
        let x = self.layer.position.x + (anchorPoint.x - lastAnchor.x) * self.layer.bounds.size.width
        let y = self.layer.position.y + (anchorPoint.y - lastAnchor.y) * self.layer.bounds.size.height
        self.layer.position = CGPoint(x: x, y: y)
        
        switch self.popoverType {
        case .up, .down, .auto:
            frame.size.height += self.arrowSize.height
        case .left, .right:
            frame.size.width += self.arrowSize.height
        }
        
        self.frame = frame
    }
    
    func createHighlightLayer(fromView: UIView, inView: UIView) {
        let path = UIBezierPath(rect: inView.bounds)
        let highlightRect = inView.convert(fromView.frame, from: fromView.superview)
        let highlightPath = UIBezierPath(roundedRect: highlightRect, cornerRadius: self.highlightCornerRadius)
        path.append(highlightPath)
        path.usesEvenOddFillRule = true
        
        let fillLayer = CAShapeLayer()
        fillLayer.path = path.cgPath
        fillLayer.fillRule = CAShapeLayerFillRule.evenOdd
        fillLayer.fillColor = self.blackOverlayColor.cgColor
        self.blackOverlay.layer.addSublayer(fillLayer)
    }
    
    func show() {
        self.setNeedsDisplay()
        switch self.popoverType {
        case .up:
            self.contentView.frame.origin.y = 0.0
        case .down, .auto:
            self.contentView.frame.origin.y = self.arrowSize.height
        case .left, .right:
            self.contentView.frame.origin.x = 0
        }
        self.addSubview(self.contentView)
        self.containerView.addSubview(self)
        
        self.create()
        self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
        self.willShowHandler?()
        UIView.animate(
            withDuration: self.animationIn,
            delay: 0,
            usingSpringWithDamping: self.springDamping,
            initialSpringVelocity: self.initialSpringVelocity,
            options: UIView.AnimationOptions(),
            animations: {
                self.transform = CGAffineTransform.identity
            }){ _ in
                self.didShowHandler?()
            }
        UIView.animate(
            withDuration: self.animationIn / 3,
            delay: 0,
            options: .curveLinear,
            animations: {
                self.blackOverlay.alpha = 1
            }, completion: nil)
    }
    
    var isCloseToCornerLeftArrow: Bool {
        return self.arrowShowPoint.x < self.frame.origin.x + arrowSize.width/2 + cornerRadius
    }
    
    var isCloseToCornerRightArrow: Bool {
        return self.arrowShowPoint.x > (self.frame.origin.x + self.bounds.width) - arrowSize.width/2 - cornerRadius
    }
    
    var isCornerLeftArrow: Bool {
        return self.arrowShowPoint.x == self.frame.origin.x
    }
    
    var isCornerRightArrow: Bool {
        return self.arrowShowPoint.x == self.frame.origin.x + self.bounds.width
    }
    
    var isBehindCornerLeftArrow: Bool {
        return self.arrowShowPoint.x < self.frame.origin.x
    }
    
    var isBehindCornerRightArrow: Bool {
        return self.arrowShowPoint.x > self.frame.origin.x + self.bounds.width
    }
    
    func radians(_ degrees: CGFloat) -> CGFloat {
        return CGFloat.pi * degrees / 180
    }
}
